package com.gitee.zhanyao.codebase.java.encryption.util;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.Security;
import java.util.Base64;

/**
 * AES 加解密工具类
 * AES的区块长度固定为128 比特，密钥长度则可以是 128，192 或 256 比特；
 * 而 Rijndael 使用的密钥和区块长度可以是 32 位的整数倍，以 128 位为下限，256 比特为上限。包括 AES-ECB,AES-CBC,AES-CTR,AES-OFB,AES-CFB
 * <p>
 * AES/CBC/PKCS5Padding："算法/模式/补码方式"
 * <p>
 * 算法可选值：AES
 * 模式可选值：ECB、CBC、CTR、OFB、CFB
 * 补码方式可选值：PKCS5Padding、PKCS7Padding、NoPadding
 *
 * @author zhanyao
 */
public class AESUtil {

    /**
     * AES 加密
     *
     * @param src     待加密字符串
     * @param pattern 加密算法
     * @param key     秘钥
     * @return
     * @throws Exception
     */
    public static String encrypt(String src, String pattern, String key) throws Exception {
        byte[] raw = key.getBytes(StandardCharsets.UTF_8);
        return encrypt(src, pattern, raw, null);
    }

    /**
     * AES 加密
     *
     * @param src     待加密字符串
     * @param pattern 加密算法
     * @param key     秘钥
     * @param iv      偏移量
     * @return
     * @throws Exception
     */
    public static String encrypt(String src, String pattern, String key, String iv) throws Exception {
        byte[] raw = key.getBytes(StandardCharsets.UTF_8);
        byte[] ivBytes = null;
        if (iv != null) {
            ivBytes = iv.getBytes(StandardCharsets.UTF_8);
        }
        return encrypt(src, pattern, raw, ivBytes);
    }

    /**
     * AES 加密
     *
     * @param src      待加密字符串
     * @param pattern  加密模式
     * @param keyBytes 秘钥字节数组
     * @param ivBytes  偏移量字节数组
     * @return
     * @throws Exception
     */
    public static String encrypt(String src, String pattern, byte[] keyBytes, byte[] ivBytes) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        SecretKeySpec skeySpec = new SecretKeySpec(keyBytes, "AES");
        Cipher cipher = Cipher.getInstance(pattern, "BC");
        if (ivBytes == null) {
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
        } else {
            IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivParameterSpec);
        }
        byte[] encrypted = cipher.doFinal(src.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(encrypted);
    }


    /**
     * AES 解密
     *
     * @param src     待解密字符串
     * @param pattern 加密算法
     * @param key     秘钥
     * @return
     * @throws Exception
     */
    public static String decrypt(String src, String pattern, String key) throws Exception {
        byte[] raw = key.getBytes(StandardCharsets.UTF_8);
        return encrypt(src, pattern, raw, null);
    }

    /**
     * AES 解密
     *
     * @param src     待解密字符串
     * @param pattern 加密算法
     * @param key     秘钥
     * @param iv      偏移量
     * @return
     * @throws Exception
     */
    public static String decrypt(String src, String pattern, String key, String iv) throws Exception {
        byte[] raw = key.getBytes(StandardCharsets.UTF_8);
        byte[] ivBytes = null;
        if (iv != null) {
            ivBytes = iv.getBytes(StandardCharsets.UTF_8);
        }
        return encrypt(src, pattern, raw, ivBytes);
    }

    /**
     * AES 解密
     *
     * @param src      待解密字符串
     * @param pattern  加密模式
     * @param keyBytes 秘钥字节数组
     * @param ivBytes  偏移量字节数组
     * @return
     * @throws Exception
     */
    public static String decrypt(String src, String pattern, byte[] keyBytes, byte[] ivBytes) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        SecretKeySpec skeySpec = new SecretKeySpec(keyBytes, "AES");
        Cipher cipher = Cipher.getInstance(pattern, "BC");
        if (ivBytes == null) {
            cipher.init(Cipher.DECRYPT_MODE, skeySpec);
        } else {
            IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivParameterSpec);
        }
        byte[] encrypted = Base64.getDecoder().decode(src);
        byte[] original = cipher.doFinal(encrypted);
        return new String(original, StandardCharsets.UTF_8);
    }

}
